<template>
  <div class="p-6 bg-white rounded-lg shadow-lg">
    <h3 class="text-xl font-bold mb-4 text-gray-800">requestAnimationFrame 与渲染帧演示</h3>
    <div class="space-y-4">
      <div class="flex flex-wrap gap-2">
        <button
          @click="runRafDemo"
          class="px-4 py-2 bg-pink-500 text-white rounded hover:bg-pink-600 transition-colors"
        >
          RAF 演示
        </button>
        <button
          @click="runAnimationDemo"
          class="px-4 py-2 bg-cyan-500 text-white rounded hover:bg-cyan-600 transition-colors"
        >
          动画演示
        </button>
        <button
          @click="runPerformanceDemo"
          class="px-4 py-2 bg-amber-500 text-white rounded hover:bg-amber-600 transition-colors"
        >
          性能对比
        </button>
        <button
          @click="stopAnimation"
          class="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600 transition-colors"
        >
          停止动画
        </button>
        <button
          @click="clearLogs"
          class="px-4 py-2 bg-gray-500 text-white rounded hover:bg-gray-600 transition-colors"
        >
          清空日志
        </button>
      </div>

      <!-- 动画演示区域 -->
      <div class="bg-gray-50 p-4 rounded-lg">
        <h4 class="font-semibold mb-2">动画演示：</h4>
        <div class="animation-container">
          <div ref="rafBox" class="animation-box raf-box">
            <span class="text-white text-sm font-bold">RAF</span>
          </div>
          <div ref="timeoutBox" class="animation-box timeout-box">
            <span class="text-white text-sm font-bold">setTimeout</span>
          </div>
        </div>
        <div class="mt-2 text-sm text-gray-600">
          <p>🟢 上方：requestAnimationFrame 驱动的动画（流畅）</p>
          <p>🔵 下方：setTimeout 驱动的动画（可能不流畅）</p>
        </div>
      </div>

      <div class="bg-gray-100 p-4 rounded-lg">
        <h4 class="font-semibold mb-2">执行日志：</h4>
        <div class="execution-log">
          <div
            v-for="(log, index) in logs"
            :key="index"
            :class="getLogClass(log.type)"
            class="py-1 px-2 mb-1 rounded"
          >
            <span class="font-mono text-sm">{{ log.message }}</span>
            <span class="text-xs opacity-75 ml-2">({{ log.type }})</span>
          </div>
        </div>
      </div>

      <div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
        <div class="bg-pink-50 p-4 rounded-lg">
          <h4 class="font-semibold mb-2">requestAnimationFrame 特点：</h4>
          <ul class="text-sm space-y-1">
            <li>• 与浏览器刷新率同步（通常 60fps）</li>
            <li>• 在每次重绘前执行</li>
            <li>• 页面隐藏时自动暂停</li>
            <li>• 适合做动画和视觉更新</li>
            <li>• 比 setTimeout 更节能</li>
          </ul>
        </div>

        <div class="bg-cyan-50 p-4 rounded-lg">
          <h4 class="font-semibold mb-2">渲染流程：</h4>
          <ul class="text-sm space-y-1">
            <li>1. 执行宏任务</li>
            <li>2. 执行所有微任务</li>
            <li>3. 执行 requestAnimationFrame</li>
            <li>4. 重新计算样式</li>
            <li>5. 重新布局</li>
            <li>6. 重新绘制</li>
          </ul>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onUnmounted, nextTick } from 'vue'

interface LogEntry {
  message: string
  type: 'sync' | 'microtask' | 'macrotask' | 'raf' | 'render'
  timestamp: number
}

const logs = ref<LogEntry[]>([])
const rafBox = ref<HTMLElement>()
const timeoutBox = ref<HTMLElement>()

let rafId: number | null = null
let timeoutId: number | null = null
let startTime = 0
let frameCount = 0

const addLog = (message: string, type: LogEntry['type']) => {
  logs.value.push({
    message,
    type,
    timestamp: Date.now(),
  })
}

const getLogClass = (type: string) => {
  const classes = {
    sync: 'bg-green-100 border-l-4 border-green-500',
    microtask: 'bg-yellow-100 border-l-4 border-yellow-500',
    macrotask: 'bg-red-100 border-l-4 border-red-500',
    raf: 'bg-pink-100 border-l-4 border-pink-500',
    render: 'bg-cyan-100 border-l-4 border-cyan-500',
  }
  return classes[type as keyof typeof classes] || ''
}

const clearLogs = () => {
  logs.value = []
}

const stopAnimation = () => {
  if (rafId) {
    cancelAnimationFrame(rafId)
    rafId = null
  }
  if (timeoutId) {
    clearTimeout(timeoutId)
    timeoutId = null
  }
  addLog('动画已停止', 'sync')
}

const runRafDemo = () => {
  clearLogs()

  addLog('1. RAF 演示开始', 'sync')

  // 宏任务
  setTimeout(() => {
    addLog('4. setTimeout 宏任务', 'macrotask')
  }, 0)

  // 微任务
  Promise.resolve().then(() => {
    addLog('2. Promise 微任务', 'microtask')
  })

  // nextTick
  nextTick(() => {
    addLog('3. nextTick', 'microtask')
  })

  // requestAnimationFrame
  requestAnimationFrame((timestamp) => {
    addLog(`5. RAF 回调执行 (时间戳: ${Math.round(timestamp)})`, 'raf')

    // RAF 中的微任务
    Promise.resolve().then(() => {
      addLog('6. RAF 中的 Promise', 'microtask')
    })

    // RAF 中的宏任务
    setTimeout(() => {
      addLog('7. RAF 中的 setTimeout', 'macrotask')
    }, 0)
  })

  addLog('同步代码执行完毕', 'sync')
}

const runAnimationDemo = () => {
  clearLogs()
  stopAnimation()

  if (!rafBox.value || !timeoutBox.value) return

  startTime = performance.now()
  frameCount = 0

  addLog('开始动画演示', 'sync')

  // RAF 动画
  const rafAnimate = (timestamp: number) => {
    if (!rafBox.value) return

    const elapsed = timestamp - startTime
    const progress = (elapsed % 2000) / 2000 // 2秒循环
    const x = progress * 250 // 移动250px（考虑到盒子宽度120px）

    rafBox.value.style.transform = `translateX(${x}px)`

    frameCount++
    if (frameCount % 60 === 0) {
      // 每60帧记录一次
      addLog(`RAF 动画帧: ${frameCount}`, 'raf')
    }

    rafId = requestAnimationFrame(rafAnimate)
  }

  rafId = requestAnimationFrame(rafAnimate)

  // setTimeout 动画（16.67ms ≈ 60fps）
  let timeoutFrameCount = 0
  const timeoutAnimate = () => {
    if (!timeoutBox.value) return

    const elapsed = performance.now() - startTime
    const progress = (elapsed % 2000) / 2000
    const x = progress * 250

    timeoutBox.value.style.transform = `translateX(${x}px)`

    timeoutFrameCount++
    if (timeoutFrameCount % 60 === 0) {
      addLog(`setTimeout 动画帧: ${timeoutFrameCount}`, 'macrotask')
    }

    timeoutId = setTimeout(timeoutAnimate, 16.67) as unknown as number
  }

  timeoutId = setTimeout(timeoutAnimate, 16.67) as unknown as number
}

const runPerformanceDemo = () => {
  clearLogs()

  addLog('性能对比演示开始', 'sync')

  const iterations = 1000
  let rafCount = 0
  let timeoutCount = 0

  const startRafTime = performance.now()

  // RAF 性能测试
  const rafPerformance = () => {
    rafCount++

    if (rafCount < iterations) {
      requestAnimationFrame(rafPerformance)
    } else {
      const rafTime = performance.now() - startRafTime
      addLog(`RAF ${iterations}次调用耗时: ${rafTime.toFixed(2)}ms`, 'raf')

      // 开始 setTimeout 测试
      const startTimeoutTime = performance.now()
      const timeoutPerformance = () => {
        timeoutCount++

        if (timeoutCount < iterations) {
          setTimeout(timeoutPerformance, 0)
        } else {
          const timeoutTime = performance.now() - startTimeoutTime
          addLog(`setTimeout ${iterations}次调用耗时: ${timeoutTime.toFixed(2)}ms`, 'macrotask')

          const diff = timeoutTime - rafTime
          addLog(
            `性能差异: ${diff > 0 ? 'setTimeout 慢 ' + diff.toFixed(2) : 'RAF 慢 ' + Math.abs(diff).toFixed(2)}ms`,
            'render',
          )
        }
      }

      setTimeout(timeoutPerformance, 0)
    }
  }

  requestAnimationFrame(rafPerformance)
}

onUnmounted(() => {
  stopAnimation()
})
</script>

<style scoped lang="scss">
.animation-container {
  height: 120px;
  position: relative;
  background:
    linear-gradient(45deg, #f0f0f0 25%, transparent 25%),
    linear-gradient(-45deg, #f0f0f0 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #f0f0f0 75%),
    linear-gradient(-45deg, transparent 75%, #f0f0f0 75%);
  background-size: 20px 20px;
  background-position:
    0 0,
    0 10px,
    10px -10px,
    -10px 0px;
  border-radius: 8px;
  overflow: hidden;
}

.animation-box {
  width: 120px;
  height: 50px;
  border-radius: 8px;
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: bold;
  transition: none;
  text-align: center;

  &.raf-box {
    top: 10px;
    background: linear-gradient(135deg, #ec4899, #be185d);
    box-shadow: 0 4px 6px rgba(236, 72, 153, 0.3);
  }

  &.timeout-box {
    bottom: 10px;
    background: linear-gradient(135deg, #06b6d4, #0891b2);
    box-shadow: 0 4px 6px rgba(6, 182, 212, 0.3);
  }
}

.execution-log {
  max-height: 300px;
  overflow-y: auto;

  &::-webkit-scrollbar {
    width: 6px;
  }

  &::-webkit-scrollbar-track {
    background: #f1f1f1;
    border-radius: 3px;
  }

  &::-webkit-scrollbar-thumb {
    background: #c1c1c1;
    border-radius: 3px;

    &:hover {
      background: #a8a8a8;
    }
  }
}
</style>
